; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=correlated-propagation < %s | FileCheck %s

declare void @llvm.trap()
declare {i8, i1} @llvm.uadd.with.overflow(i8, i8)
declare {i8, i1} @llvm.sadd.with.overflow(i8, i8)
declare {i8, i1} @llvm.usub.with.overflow(i8, i8)
declare {i8, i1} @llvm.ssub.with.overflow(i8, i8)
declare {i8, i1} @llvm.umul.with.overflow(i8, i8)
declare {i8, i1} @llvm.smul.with.overflow(i8, i8)

define i1 @uadd_ov_false(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @uadd_ov_false(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ugt i8 [[X]], -102
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.uadd.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp ugt i8 %x, 154
  store i1 %c1, ptr %pc
  %c2 = icmp ugt i8 %x, 155
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @uadd_ov_true(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @uadd_ov_true(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ugt i8 [[X]], -100
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.uadd.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp ugt i8 %x, 156
  store i1 %c1, ptr %pc
  %c2 = icmp ugt i8 %x, 155
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @sadd_ov_false(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @sadd_ov_false(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp sgt i8 [[X]], 26
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.sadd.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp sgt i8 %x, 26
  store i1 %c1, ptr %pc
  %c2 = icmp sgt i8 %x, 27
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @sadd_ov_true(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @sadd_ov_true(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ugt i8 [[X]], 28
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.sadd.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp sgt i8 %x, 28
  store i1 %c1, ptr %pc
  %c2 = icmp sgt i8 %x, 27
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @usub_ov_false(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @usub_ov_false(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[X]], 101
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.usub.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp ult i8 %x, 101
  store i1 %c1, ptr %pc
  %c2 = icmp ult i8 %x, 100
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @usub_ov_true(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @usub_ov_true(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[X]], 99
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.usub.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp ult i8 %x, 99
  store i1 %c1, ptr %pc
  %c2 = icmp ult i8 %x, 100
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @ssub_ov_false(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @ssub_ov_false(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp slt i8 [[X]], -27
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.ssub.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp slt i8 %x, -27
  store i1 %c1, ptr %pc
  %c2 = icmp slt i8 %x, -28
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @ssub_ov_true(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @ssub_ov_true(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[X]], -29
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.ssub.with.overflow(i8 %x, i8 100)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp slt i8 %x, -29
  store i1 %c1, ptr %pc
  %c2 = icmp slt i8 %x, -28
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @umul_ov_false(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @umul_ov_false(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ugt i8 [[X]], 24
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.umul.with.overflow(i8 %x, i8 10)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp ugt i8 %x, 24
  store i1 %c1, ptr %pc
  %c2 = icmp ugt i8 %x, 25
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @umul_ov_true(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @umul_ov_true(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp ugt i8 [[X]], 26
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.umul.with.overflow(i8 %x, i8 10)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp ugt i8 %x, 26
  store i1 %c1, ptr %pc
  %c2 = icmp ugt i8 %x, 25
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

; Signed mul is constrained from both sides.
define i1 @smul_ov_false_bound1(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @smul_ov_false_bound1(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp slt i8 [[X]], -11
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.smul.with.overflow(i8 %x, i8 10)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp slt i8 %x, -11
  store i1 %c1, ptr %pc
  %c2 = icmp slt i8 %x, -12
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @smul_ov_false_bound2(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @smul_ov_false_bound2(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp sgt i8 [[X]], 11
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.smul.with.overflow(i8 %x, i8 10)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %c1 = icmp sgt i8 %x, 11
  store i1 %c1, ptr %pc
  %c2 = icmp sgt i8 %x, 12
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

; Can't use slt/sgt to test for a hole in the range, check equality instead.
define i1 @smul_ov_true_bound1(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @smul_ov_true_bound1(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp eq i8 [[X]], -13
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.smul.with.overflow(i8 %x, i8 10)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp eq i8 %x, -13
  store i1 %c1, ptr %pc
  %c2 = icmp eq i8 %x, -12
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @smul_ov_true_bound2(i8 %x, ptr %px, ptr %pc) {
; CHECK-LABEL: @smul_ov_true_bound2(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    store i8 [[VAL]], ptr [[PX:%.*]], align 1
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[OVERFLOW:%.*]], label [[TRAP:%.*]]
; CHECK:       overflow:
; CHECK-NEXT:    [[C1:%.*]] = icmp eq i8 [[X]], 13
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 false
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.smul.with.overflow(i8 %x, i8 10)
  %val = extractvalue {i8, i1} %val_ov, 0
  store i8 %val, ptr %px
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %overflow, label %trap

overflow:
  %c1 = icmp eq i8 %x, 13
  store i1 %c1, ptr %pc
  %c2 = icmp eq i8 %x, 12
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @uadd_val(i8 %x, ptr %pc) {
; CHECK-LABEL: @uadd_val(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp ugt i8 [[VAL]], 100
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.uadd.with.overflow(i8 %x, i8 100)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp ugt i8 %val, 100
  store i1 %c1, ptr %pc
  %c2 = icmp uge i8 %val, 100
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @sadd_val(i8 %x, ptr %pc) {
; CHECK-LABEL: @sadd_val(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp sgt i8 [[VAL]], -28
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.sadd.with.overflow(i8 %x, i8 100)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp sgt i8 %val, -28
  store i1 %c1, ptr %pc
  %c2 = icmp sge i8 %val, -28
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @usub_val(i8 %x, ptr %pc) {
; CHECK-LABEL: @usub_val(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[VAL]], -101
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.usub.with.overflow(i8 %x, i8 100)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp ult i8 %val, 155
  store i1 %c1, ptr %pc
  %c2 = icmp ule i8 %val, 155
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @ssub_val(i8 %x, ptr %pc) {
; CHECK-LABEL: @ssub_val(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 100)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp slt i8 [[VAL]], 27
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.ssub.with.overflow(i8 %x, i8 100)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp slt i8 %val, 27
  store i1 %c1, ptr %pc
  %c2 = icmp sle i8 %val, 27
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @umul_val(i8 %x, ptr %pc) {
; CHECK-LABEL: @umul_val(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[VAL]], -6
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.umul.with.overflow(i8 %x, i8 10)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp ult i8 %val, 250
  store i1 %c1, ptr %pc
  %c2 = icmp ule i8 %val, 250
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @smul_val_bound1(i8 %x, ptr %pc) {
; CHECK-LABEL: @smul_val_bound1(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp slt i8 [[VAL]], 120
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.smul.with.overflow(i8 %x, i8 10)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp slt i8 %val, 120
  store i1 %c1, ptr %pc
  %c2 = icmp sle i8 %val, 120
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}

define i1 @smul_val_bound2(i8 %x, ptr %pc) {
; CHECK-LABEL: @smul_val_bound2(
; CHECK-NEXT:    [[VAL_OV:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[X:%.*]], i8 10)
; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 1
; CHECK-NEXT:    br i1 [[OV]], label [[TRAP:%.*]], label [[NO_OVERFLOW:%.*]]
; CHECK:       no_overflow:
; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i8, i1 } [[VAL_OV]], 0
; CHECK-NEXT:    [[C1:%.*]] = icmp sgt i8 [[VAL]], -120
; CHECK-NEXT:    store i1 [[C1]], ptr [[PC:%.*]], align 1
; CHECK-NEXT:    ret i1 true
; CHECK:       trap:
; CHECK-NEXT:    call void @llvm.trap()
; CHECK-NEXT:    unreachable
;
  %val_ov = call {i8, i1} @llvm.smul.with.overflow(i8 %x, i8 10)
  %ov = extractvalue {i8, i1} %val_ov, 1
  br i1 %ov, label %trap, label %no_overflow

no_overflow:
  %val = extractvalue {i8, i1} %val_ov, 0
  %c1 = icmp sgt i8 %val, -120
  store i1 %c1, ptr %pc
  %c2 = icmp sge i8 %val, -120
  ret i1 %c2

trap:
  call void @llvm.trap()
  unreachable
}
